package com.clay.appointment.Utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.clay.appointment.exception.ExceptionEnum;
import com.clay.appointment.exception.GlobalException;
import com.clay.appointment.vo.LoginVo;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * jwt工具类
 *
 * @author Yeung Wah
 * @since 2021/5/31
 */
@Component
public class TokenUtils {


    @Resource
    private RedisTemplate<String, LoginVo> redisTemplate;

    /**
     * redis需要更新token的时间
     */
    private static final long REDIS_TIMEOUT_CHECK_TIME = 60 * 25;
    /**
     * 超时时间
     */
    private static final long REDIS_TIMEOUT = 30 * 60;
    /**
     * 用户id
     */
    private static final String USER_ID = "userId";

    /**
     * 密钥
     */
    private static final String SECRET = "ZGVybWF0b2dseXBoaWNzdW5jb3B5cmlnaHRhYmxlc21pc2Nvbmp1Z2F0ZWRseQ==";
    /**
     * 发布人/签发者
     */
    private static final String ISSUER = "clay";
    /**
     * 超时时间 （毫秒）
     */
    private static final Long TIMEOUT = 1000 * 60 * 20L;
    /**
     * 加密算法
     */
    private static final Algorithm ALGORITHM = Algorithm.HMAC256(SECRET);
    /**
     * decoder
     */
    private static final JWTVerifier VERIFIER = JWT.require(ALGORITHM).withIssuer(ISSUER).build();

    /**
     * 超时
     */

    private static final String REDIS_HAS_TIMEOUT = "token已经超时,请重新登录";

    /**
     * 校验成功
     */
    private static final String SUCCESS = "校验成功";
    /**
     * 校验失败
     */
    private static final String FAILED = "校验失败";


    /**
     * 解密
     *
     * @param token token
     * @return 参数
     */
    public Map<String, String> decode(String token, ServerWebExchange exchange) {
        DecodedJWT verify = null;
        try {
            verify = VERIFIER.verify(token);
        } catch (JWTVerificationException e) {
            GlobalException.throwOut(ExceptionEnum.AUTH_FAILED);
        }

        Map<String, Claim> claims = verify.getClaims();
        String ip = claims.get("ip").asString();
        if (!ip.equals(webFluxUtil.getIp(exchange))) {
            GlobalException.throwOut(ExceptionEnum.IP_DIFFERENT);
        }

        Map<String, String> map = new HashMap<>();
        claims.forEach((k, v) -> {
            map.put(k, v.asString());
        });

        map.remove("iss");
        map.remove("ip");
        return map;
    }

    /**
     * 校验
     *
     * @param token token
     * @return 状态
     */
    public String verify(String token) {
        DecodedJWT verify;
        try {
            verify = VERIFIER.verify(token);
        } catch (JWTVerificationException e) {
            return FAILED;
        }

        Map<String, Claim> claims = verify.getClaims();

        ValueOperations<String, LoginVo> operations = redisTemplate.opsForValue();
        LoginVo loginVo = operations.get(token);
        String userId = claims.get("userId").asString();
        if (userId.equals(loginVo.getOpenid())) {
            //校验成功判断redis过期时间，如果时间剩下不到五分钟
            Long time = operations.getOperations().getExpire(token);
            if (time >= REDIS_TIMEOUT_CHECK_TIME && time < REDIS_TIMEOUT) {
                //删除redis中的token键值对
                operations.getOperations().delete(token);
                operations.set(token, loginVo, 30, TimeUnit.MINUTES);
                return SUCCESS;
            } else if (time > REDIS_TIMEOUT) {
                return REDIS_HAS_TIMEOUT;
            }
        }

        return FAILED;
    }


}